//
// detail/call_stack.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef ASIO_DETAIL_CALL_STACK_HPP
#define ASIO_DETAIL_CALL_STACK_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/tss_ptr.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {
    namespace detail {

// Helper class to determine whether or not the current thread is inside an
// invocation of io_context::run() for a specified io_context object.
        template<typename Key, typename Value = unsigned char>
        class call_stack {
        public:
            // Context class automatically pushes the key/value pair on to the stack.
            class context
                    : private noncopyable {
            public:
                // Push the key on to the stack.
                explicit context(Key *k)
                        : key_(k),
                          next_(call_stack<Key, Value>::top_) {
                    value_ = reinterpret_cast<unsigned char *>(this);
                    call_stack<Key, Value>::top_ = this;
                }

                // Push the key/value pair on to the stack.
                context(Key *k, Value &v)
                        : key_(k),
                          value_(&v),
                          next_(call_stack<Key, Value>::top_) {
                    call_stack<Key, Value>::top_ = this;
                }

                // Pop the key/value pair from the stack.
                ~context() {
                    call_stack<Key, Value>::top_ = next_;
                }

                // Find the next context with the same key.
                Value *next_by_key() const {
                    context *elem = next_;
                    while (elem) {
                        if (elem->key_ == key_)
                            return elem->value_;
                        elem = elem->next_;
                    }
                    return 0;
                }

            private:
                friend class call_stack<Key, Value>;

                // The key associated with the context.
                Key *key_;

                // The value associated with the context.
                Value *value_;

                // The next element in the stack.
                context *next_;
            };

            friend class context;

            // Determine whether the specified owner is on the stack. Returns address of
            // key if present, 0 otherwise.
            static Value *contains(Key *k) {
                context *elem = top_;
                while (elem) {
                    if (elem->key_ == k)
                        return elem->value_;
                    elem = elem->next_;
                }
                return 0;
            }

            // Obtain the value at the top of the stack.
            static Value *top() {
                context *elem = top_;
                return elem ? elem->value_ : 0;
            }

        private:
            // The top of the stack of calls for the current thread.
            static tss_ptr <context> top_;
        };

        template<typename Key, typename Value>
        tss_ptr<typename call_stack<Key, Value>::context>
                call_stack<Key, Value>::top_;

    } // namespace detail
} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_DETAIL_CALL_STACK_HPP
